﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;

namespace GameNetworking
{
    public class UserMessageReceivedEventArgs : EventArgs
    {
        // The argument we're interested in is a message describing the event
        private string EventMsg;

        // Property for retrieving and setting the event message
        public string EventMessage
        {
            get { return EventMsg; }
            set { EventMsg = value; }
        }

        // Constructor for setting the event message
        public UserMessageReceivedEventArgs(string strEventMsg)
        {
            EventMsg = strEventMsg;
        }
    }

    public delegate void UserMessageReceivedEventHandler(object sender, UserMessageReceivedEventArgs e);

    public class User
    {
        #region Private Members

        private string strUserName;
        private string strServer;
        private int iPort;
        private bool connected;
        private List<string> log;
        

        private StreamWriter swSender;
        private StreamReader srReceiver;
        
        private Thread thrMessaging;

        private TcpClient tcpServer;       
        
        public static event UserMessageReceivedEventHandler MessageReceived;
        public static UserMessageReceivedEventArgs e;

        #endregion

        #region Properties

        public bool Connected
        {
            get { return connected; }
            set { connected = value; }
        }        
                
        public TcpClient Server
        {
            get { return tcpServer; }
            set { tcpServer = value; }
        }

        public string UserName
        {
            get { return strUserName; }
            set { strUserName = value; }
        } 

        #endregion

        #region Constructors

        public User(string server, int port, string username)
        {
            log = new List<string>();
            connected = false;
            strUserName = username;
            strServer = server;
            iPort = port;
        }

        public User(TcpClient server)
        {
            tcpServer = server;
        }

        #endregion 

        #region Methods

        public void Connect()
        {
            if (connected)
            {
                // Start the thread for receiving messages and further communication
                thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
                thrMessaging.Start();
            }
            else
            {
                tcpServer = new TcpClient();
                tcpServer.Connect(strServer, iPort);
                connected = true;
                SendMessage(UserName);
                thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
                thrMessaging.Start();
            }
        }

        public void Disconnect()
        {
            if (connected)
            {
                swSender.Close();
                srReceiver.Close();
                tcpServer.Close();
            }
        }

        public void SendMessage(string message)
        {
            // Send the desired username to the server
            swSender = new StreamWriter(tcpServer.GetStream());
            swSender.WriteLine(message);
            swSender.Flush();

        }

        private void ReceiveMessages()
        {
            // Receive the response from the server
            srReceiver = new StreamReader(tcpServer.GetStream());
            // If the first character of the response is 1, connection was successful
            string ConResponse = srReceiver.ReadLine();

            e = new UserMessageReceivedEventArgs(ConResponse);

            OnMessageReceived(e);

            // If the first character is a 1, connection was successful
            if (ConResponse == UserName)
            {
                // Update the form to tell it we are now connected
                UpdateLog("Connected Successfully");
            }
            else // If the first character is not a 1 (probably a 0), the connection was unsuccessful
            {
                string Reason = "Not Connected: ";
                // Extract the reason out of the response message. The reason starts at the 3rd character
                Reason += ConResponse.Substring(2, ConResponse.Length - 2);
                // Update the form with the reason why we couldn't connect
                // Exit the method
                return;
            }
            // While we are successfully connected, read incoming lines from the server
            while (Connected)
            {
                srReceiver = new StreamReader(tcpServer.GetStream());
                // If the first character of the response is 1, connection was successful
                string response = srReceiver.ReadLine();

                // Show the messages in the log TextBox
                e = new UserMessageReceivedEventArgs(response);
                OnMessageReceived(e);

            }
        }

        #endregion

        #region User Helpers

        public static User Parse(object user)
        {
            try
            {
                return user as User;
            }
            catch (Exception)
            {
                return null;
            }
        }

        // This method is called from a different thread in order to update the log TextBox
        private void UpdateLog(string strMessage)
        {
            // Append text also scrolls the TextBox to the bottom each time
            log.Add(strMessage + "\r\n");
        }

        #endregion

        #region Events

        // This is called when we want to raise the StatusChanged event
        public static void OnMessageReceived(UserMessageReceivedEventArgs e)
        {
            UserMessageReceivedEventHandler messageHandler = MessageReceived;
            if (messageHandler != null)
            {
                // Invoke the delegate
                messageHandler(null, e);
            }
        }

        #endregion
    }

    public class UserMessage
    {
        public UserMessage(string username, string message)
        {
            strUserName = username;
            strMessage = message;
        }
        string strUserName;
        string strMessage;
    }
}
